sync.Pool
1.sync.Pool
01.sync.Pool介绍
1.1 是什么
sync.Pool是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”。- 个人觉得它的名字有一定的误导性,因为 Pool 里装的对象可以被无通知地被回收,可能
sync.Cache是一个更合适的名字。 Pool结构体的定义为:Pool中有两个定义的公共方法,分别是Put- 向池中添加元素;Get从池中获取元素,如果没有,则调用New生成元素,如果New未设置,则返回nil。
type Pool struct {
noCopy noCopy
local unsafe.Pointer // 本地P缓存池指针
localSize uintptr // 本地P缓存池大小
// 当池中没有可能对象时
// 会调用 New 函数构造构造一个对象
New func() interface{}
}1.2 有什么用
- 对于很多需要重复分配、回收内存的地方,
sync.Pool是一个很好的选择。 - 频繁地分配、回收内存会给 GC 带来一定的负担,严重的时候会引起 CPU 的毛刺
- 而
sync.Pool可以将暂时不用的对象缓存起来,待下次需要的时候直接使用,不用再次经过内存分配 - 复用对象的内存,减轻 GC 的压力,提升系统的性能。
1.3 怎么用
- 首先,
sync.Pool是协程安全的,这对于使用者来说是极其方便的。 - 使用前,设置好对象的
New函数,用于在Pool里没有缓存的对象时,创建一个。 - 之后,在程序的任何地方、任何时候仅通过
Get()、Put()方法就可以取、还对象了。
package main
import (
"fmt"
"sync"
)
var pool *sync.Pool
type Person struct {
Name string
}
func initPool() {
pool = &sync.Pool {
New: func() interface{} {
fmt.Println("Creating a new Person")
return new(Person)
},
}
}
func main() {
initPool()
p := pool.Get().(*Person) // get获取不到就会调用方法,创建一个
p.Name = "first"
pool.Put(p) // 使用 Put方法将对象放回 pool池子中
fmt.Println("Pool 里已有一个对象:&{first},调用 Get: ", pool.Get().(*Person))
fmt.Println("Pool 没有对象了,调用 Get: ", pool.Get().(*Person)) // 获取后再次获取就没有了,会再次创建
}
/*
Creating a new Person
Pool 里已有一个对象:&{first},调用 Get: &{first}
Creating a new Person
Pool 没有对象了,调用 Get: &{}
*/1.4 Get
Pool会为每个P维护一个本地池,P的本地池分为 私有池private和共享池shared。- 私有池中的元素只能本地
P使用,共享池中的元素可能会被其他P偷走 - 所以使用私有池
private时不用加锁,而使用共享池shared时需加锁。 Get会优先查找本地private,再查找本地shared,最后查找其他P的shared- 如果以上全部没有可用元素,最后会调用
New函数获取新元素。
1.5 PUT
Put优先把元素放在private池中;- 如果
private不为空,则放在shared池中 - 有趣的是,在入池之前,该元素有 1/4 可能被丢掉。
02.gin中的Context pool
- 在
web应用中,后台在处理用户的每条请求时都会为当前请求创建一个上下文环境Context,用于存储请求信息及相应信息等。 Context满足长生命周期的特点,且用户请求也是属于并发环境,所以对于线程安全的Pool非常适合用来维护Context的临时对象池。Gin在结构体Engine中定义了一个pool:
type Engine struct {
// ... 省略了其他字段
pool sync.Pool
}- 初始化
engine时定义了pool的New函数:
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
// allocateContext
func (engine *Engine) allocateContext() *Context {
// 构造新的上下文对象
return &Context{engine: engine}
}- ServeHttp
// 从 pool 中获取,并转化为 *Context
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset() // reset
engine.handleHTTPRequest(c)
// 再扔回 pool 中
engine.pool.Put(c)sync.Pool
http://coderedeng.github.io/2021/02/21/Go进阶 - sync_Pool /